﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Kinect;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Windows.Threading;

namespace SSS.Kinect
{
    /// <summary>
    /// Interaktionslogik für KinectBodyMeasurement.xaml
    /// </summary>
    public partial class KinectBodyMeasurement : UserControl, INotifyPropertyChanged
    {
        public KinectBodyMeasurement()
        {
            InitializeComponent();
        }

        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            kinectSensorChooser.KinectSensorChanged += new DependencyPropertyChangedEventHandler(kinectSensorChooser_KinectSensorChanged);
            timer.Interval = new TimeSpan(0, 0, 3);
            timer.Tick += new EventHandler(timer_Tick);
            
            if (!DesignerProperties.GetIsInDesignMode(this))
                timer.Start();

            FramePerSecondFakeTimer.Interval = new TimeSpan(333333);
            FramePerSecondFakeTimer.Tick += new EventHandler(FramePerSecondFakeTimer_Tick);
            FramePerSecondFakeTimer.Stop();

            measureTimer.Tick += new EventHandler(measureTimer_Tick);
            measureTimer.Stop();

            TimeForBigMessage.Tick += new EventHandler(TimeForBigMessage_Tick);
            TimeForBigMessage.Stop();
        }

        public void ShowBigRedMessage(string text)
        {
            lblBigStatus.Foreground = Brushes.Red;
            ShowBigMessage(text);

        }

        public void ShowBigGreenMessage(string text)
        {
            lblBigStatus.Foreground = Brushes.Lime;
            ShowBigMessage(text);
        }

        private void ShowBigMessage(string text)
        {
            if (TimeForBigMessage.IsEnabled)
            {
                TimeForBigMessage.Stop();                
            }
            lblBigStatus.Content = text;
            lblBigStatus.Visibility = System.Windows.Visibility.Visible;            
            TimeForBigMessage.Interval = new TimeSpan(0, 0, 5);            
            TimeForBigMessage.Start();
        }

        void TimeForBigMessage_Tick(object sender, EventArgs e)
        {
            lblBigStatus.Visibility = System.Windows.Visibility.Hidden;
            TimeForBigMessage.Stop();
        }

        void measureTimer_Tick(object sender, EventArgs e)
        {
            EndMeasurement();
        }

        void FramePerSecondFakeTimer_Tick(object sender, EventArgs e)
        {
            Random rnd = new Random();
            CurrentLengthLeftUpperArm = 19 + rnd.Next(6) + rnd.NextDouble();
            CurrentLengthLeftForeArm = 22 + rnd.Next(6) + rnd.NextDouble();
            CurrentLengthLeftShank = 42 + rnd.Next(6) + rnd.NextDouble();
            CurrentLengthLeftTigh = 52 + rnd.Next(6) + rnd.NextDouble();

            if (isLongMeasuringInProcess)
            {
                AddLengthToListIfNotNull(LengthsLeftForeArm, CurrentLengthLeftForeArm);
                AddLengthToListIfNotNull(LengthsLeftUpperArm, CurrentLengthLeftUpperArm);
                AddLengthToListIfNotNull(LengthsLeftTigh, CurrentLengthLeftTigh);
                AddLengthToListIfNotNull(LengthsLeftShank, CurrentLengthLeftShank);
            }
        }

        void timer_Tick(object sender, EventArgs e)
        {
            StartFakeMode();
            timer.Stop();
        }

        void kinectSensorChooser_KinectSensorChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            try
            {
                KinectSensor oldSensor = (KinectSensor)e.OldValue;
                StopKinect(oldSensor);
                KinectSensor newSensor = (KinectSensor)e.NewValue;

                newSensor.ColorStream.Enable();
                newSensor.SkeletonStream.Enable();
                newSensor.DepthStream.Enable();
                newSensor.AllFramesReady += new EventHandler<AllFramesReadyEventArgs>(newSensor_AllFramesReady);


                try
                {
                    newSensor.Start();
                    EndFakeMode();

                }
                catch (System.IO.IOException)
                {
                    kinectSensorChooser.AppConflictOccurred();
                }
            
            }
            catch (Exception)
            {

            }

        }

        void StartFakeMode()
        {
            m_FakeMode = true;
            fakeModeLabel.Visibility = System.Windows.Visibility.Visible;
            FramePerSecondFakeTimer.Start();
        }

        void EndFakeMode()
        {
            m_FakeMode = false;
            timer.Stop();
            FramePerSecondFakeTimer.Stop();
            fakeModeLabel.Visibility = System.Windows.Visibility.Hidden;
            InternalLengthReset();
            
        }

        // alle internen Werte zurücksetzen
        void InternalLengthReset()
        {
            CurrentLengthLeftForeArm = 0;
            CurrentLengthLeftUpperArm = 0;
            CurrentLengthLeftTigh = 0;
            CurrentLengthLeftShank = 0;
        }

        void newSensor_AllFramesReady(object sender, AllFramesReadyEventArgs e)
        {
            if (closing)
            {
                return;
            }

            Skeleton first = GetFirstSkeleton(e);

            if (first == null)
                return;

            WriteSkeletonData(first, e);
        }

        void WriteSkeletonData(Skeleton first, AllFramesReadyEventArgs e)
        {
            using (DepthImageFrame depth = e.OpenDepthImageFrame())
            {
                if (depth == null || kinectSensorChooser.Kinect == null)
                {
                    return;
                }
               
                // Körperpunkte des Skeletts setzen
                CurrentPositionHead = first.Joints[JointType.Head];
                CurrentPositionElbowLeft = first.Joints[JointType.ElbowLeft];
                CurrentPositionWristLeft = first.Joints[JointType.WristLeft];
                CurrentPositionShoulderLeft = first.Joints[JointType.ShoulderLeft];
                CurrentPositionAnkleLeft = first.Joints[JointType.AnkleLeft];
                CurrentPositionHipLeft = first.Joints[JointType.HipLeft];
                CurrentPositionKneeLeft = first.Joints[JointType.KneeLeft];

                // Längen von Körperteilen setzen
                CurrentLengthLeftForeArm = GetLengthIfTracked(CurrentPositionElbowLeft, CurrentPositionWristLeft);
                CurrentLengthLeftUpperArm = GetLengthIfTracked(CurrentPositionElbowLeft, CurrentPositionShoulderLeft);
                CurrentLengthLeftTigh = GetLengthIfTracked(CurrentPositionHipLeft, CurrentPositionKneeLeft);
                CurrentLengthLeftShank = GetLengthIfTracked(CurrentPositionKneeLeft, CurrentPositionAnkleLeft);


                // falls längere Messung
                if (isLongMeasuringInProcess)
                {
                    AddLengthToListIfNotNull(LengthsLeftForeArm, CurrentLengthLeftForeArm);
                    AddLengthToListIfNotNull(LengthsLeftUpperArm, CurrentLengthLeftUpperArm);
                    AddLengthToListIfNotNull(LengthsLeftTigh, CurrentLengthLeftTigh);
                    AddLengthToListIfNotNull(LengthsLeftShank, CurrentLengthLeftShank);
                }

            }
        }

        void AddLengthToListIfNotNull(List<double> list, double length)
        {
             if (length > 0)
                 list.Add(length);

        }


        #region Körperteillängen (auch zusammengesetzte)
        double GetLengthIfTracked(Joint a, Joint b)
        {
            if ((a.TrackingState == JointTrackingState.Tracked) && (b.TrackingState == JointTrackingState.Tracked))
            {
                return GetDistance(a.Position, b.Position);
            }
            else
                return 0;
        }
        #endregion

        double GetDistance(SkeletonPoint a, SkeletonPoint b)
        {
            return Math.Sqrt(Math.Pow((a.X - b.X), 2) + Math.Pow((a.Y - b.Y), 2) + Math.Pow((a.Z - b.Z), 2))*100;
        }

        Skeleton GetFirstSkeleton(AllFramesReadyEventArgs e)
        {
            using (SkeletonFrame skeletonFrameData = e.OpenSkeletonFrame())
            {
                if (skeletonFrameData == null)
                {
                    return null;
                }

                skeletonFrameData.CopySkeletonDataTo(allSkeletons);

                // get the first
                Skeleton first = (from s in allSkeletons
                                  where s.TrackingState == SkeletonTrackingState.Tracked
                                  select s).FirstOrDefault();

                return first;
            }
        }


        void StopKinect(KinectSensor sensor)
        {
            if (sensor != null)
            {
                sensor.Stop();
                sensor.AudioSource.Stop();
            }
        }

        #region private fields
        bool closing = false;
        const int skeletonCount = 6;
        Skeleton[] allSkeletons = new Skeleton[skeletonCount];
        DispatcherTimer timer = new DispatcherTimer();
        DispatcherTimer FramePerSecondFakeTimer = new DispatcherTimer();
        DispatcherTimer TimeForBigMessage = new DispatcherTimer();
        
        #endregion

        #region Events
        private void UserControl_Unloaded(object sender, RoutedEventArgs e)
        {
            closing = true;
            StopKinect(kinectSensorChooser.Kinect);
        }

        #endregion


        #region Propertys
        private bool m_FakeMode = true;

        public bool IsFakeMode
        {
            get { return m_FakeMode; }            
        }

        private int m_measuringTime = 10;

        public int MeasuringTime
        {
            get { return m_measuringTime; }
            set { m_measuringTime = value; }
        }
        


        private Joint m_currentPosHead;
        public Joint CurrentPositionHead
        {
            get { return m_currentPosHead; }
            set
            {
                m_currentPosHead = value;
                NotifyPropertyChanged("CurrentPositionHead");
            }
        }

        private Joint m_currentPosWristLeft;
        public Joint CurrentPositionWristLeft
        {
            get { return m_currentPosWristLeft; }
            set
            {
                m_currentPosWristLeft = value;
                NotifyPropertyChanged("CurrentPositionWristLeft");
            }
        }

        private Joint m_currentPosShoulderLeft;
        public Joint CurrentPositionShoulderLeft
        {
            get { return m_currentPosShoulderLeft; }
            set
            {
                m_currentPosShoulderLeft = value;
                NotifyPropertyChanged("CurrentPositionShoulderLeft");
            }
        }

        private Joint m_currentPosElbowLeft;
        public Joint CurrentPositionElbowLeft
        {
            get { return m_currentPosElbowLeft; }
            set
            {
                m_currentPosElbowLeft = value;
                NotifyPropertyChanged("CurrentPositionElbowLeft");
            }
        }

        private Joint m_currentPosHipLeft;
        public Joint CurrentPositionHipLeft
        {
            get { return m_currentPosHipLeft; }
            set
            {
                m_currentPosHipLeft = value;
                NotifyPropertyChanged("CurrentPositionHipLeft");
            }
        }

        private Joint m_currentPosKneeLeft;
        public Joint CurrentPositionKneeLeft
        {
            get { return m_currentPosKneeLeft; }
            set
            {
                m_currentPosKneeLeft = value;
                NotifyPropertyChanged("CurrentPositionKneeLeft");
            }
        }

        private Joint m_currentPosAnkleLeft;
        public Joint CurrentPositionAnkleLeft
        {
            get { return m_currentPosAnkleLeft; }
            set
            {
                m_currentPosAnkleLeft = value;
                NotifyPropertyChanged("CurrentPositionAnkleLeft");
            }
        }

        private double m_curLengthLeftForeArm;
        public double CurrentLengthLeftForeArm
        {
            get { return m_curLengthLeftForeArm; }
            set 
            {
                m_curLengthLeftForeArm = value;
                NotifyPropertyChanged("CurrentLengthLeftForeArm");
                NotifyPropertyChanged("CurrentLengthLeftFullArm");
            }
        }

        private double m_curLengthLeftUpperArm;
        public double CurrentLengthLeftUpperArm
        {
            get { return m_curLengthLeftUpperArm; }
            set
            {
                m_curLengthLeftUpperArm = value;
                NotifyPropertyChanged("CurrentLengthLeftUpperArm");
                NotifyPropertyChanged("CurrentLengthLeftFullArm");
            }
        }

        private double m_curLengthLeftThigh;
        public double CurrentLengthLeftTigh
        {
            get { return m_curLengthLeftThigh; }
            set {
                    m_curLengthLeftThigh = value; 
                    NotifyPropertyChanged("CurrentLengthLeftTigh");
                    NotifyPropertyChanged("CurrentLengthLeftLeg");
                }
        }

        private double m_curLengthLeftShank;
        public double CurrentLengthLeftShank
        {
            get { return m_curLengthLeftShank; }
            set
            {
                m_curLengthLeftShank = value;
                NotifyPropertyChanged("CurrentLengthLeftShank");
                NotifyPropertyChanged("CurrentLengthLeftLeg");
            }
        }

        public double CurrentLengthLeftLeg
        {
            get
            {
                if ((CurrentLengthLeftShank > 0) && (CurrentLengthLeftTigh > 0))
                    return (CurrentLengthLeftShank + CurrentLengthLeftTigh);
                else
                    return 0;
            }
        }

        public double CurrentLengthLeftFullArm
        {
            get
            {
                if ((CurrentLengthLeftForeArm > 0) && (CurrentLengthLeftUpperArm > 0))
                    return (CurrentLengthLeftForeArm + CurrentLengthLeftUpperArm);
                else
                    return 0;
            }
        }

        

        public double MeasuredLengthLeftForeArm
        {
            get
            {
                if (LengthsLeftForeArm.Count > 0)
                {
                    return LengthsLeftForeArm.AsQueryable().Average();
                }
                else
                    return 0;
            }            
        }

        public double MeasuredLengthLeftUpperArm
        {
            get
            {
                if (LengthsLeftUpperArm.Count > 0)
                {
                    return LengthsLeftUpperArm.AsQueryable().Average();
                }
                else
                    return 0;
            }
        }

        public double MeasuredLengthLeftFullArm
        {
            get
            {
                if ((MeasuredLengthLeftForeArm > 0) && (MeasuredLengthLeftUpperArm > 0))
                    return ((MeasuredLengthLeftForeArm + MeasuredLengthLeftUpperArm));
                else
                    return 0;
            }
        }

        public double MeasuredLengthLeftTigh
        {
            get
            {
                if (LengthsLeftTigh.Count > 0)
                {
                    return LengthsLeftTigh.AsQueryable().Average();
                }
                else
                    return 0;
            }
        }

        public double MeasuredLengthLeftShank
        {
            get
            {
                if (LengthsLeftShank.Count > 0)
                {
                    return LengthsLeftShank.AsQueryable().Average();
                }
                else
                    return 0;
            }
        }

        public double MeasuredLengthLeftLeg
        {
            get
            {
                if ((MeasuredLengthLeftTigh > 0) && (MeasuredLengthLeftShank > 0))
                    return ((MeasuredLengthLeftTigh + MeasuredLengthLeftShank));
                else
                    return 0;
            }
        }
        

        #endregion

        #region InteralProperties for Interval-Measurement
        List<double> LengthsLeftForeArm = new List<double>();
        List<double> LengthsLeftUpperArm = new List<double>();
        List<double> LengthsLeftShank = new List<double>();
        List<double> LengthsLeftTigh = new List<double>();

        DispatcherTimer measureTimer = new DispatcherTimer();
        bool isLongMeasuringInProcess = false;
        #endregion

        public void StartMeasurement()
        {
            
            isLongMeasuringInProcess = true;
            measureTimer.Interval = new TimeSpan(0, 0, MeasuringTime);

            // Listen leeren
            LengthsLeftForeArm.Clear();
            LengthsLeftUpperArm.Clear();
            LengthsLeftShank.Clear();
            LengthsLeftTigh.Clear();


            // Propertys refreshen für GUI
            NotifyPropertyChanged("MeasuredLengthLeftForeArm");
            NotifyPropertyChanged("MeasuredLengthLeftUpperArm");
            NotifyPropertyChanged("MeasuredLengthLeftFullArm");
            NotifyPropertyChanged("MeasuredLengthLeftShank");
            NotifyPropertyChanged("MeasuredLengthLeftTigh");
            NotifyPropertyChanged("MeasuredLengthLeftLeg");

            labelLongMeasureInProcess.Visibility = System.Windows.Visibility.Visible;
            measureTimer.Start();
        }

        public void EndMeasurement()
        {
            isLongMeasuringInProcess = false;
            labelLongMeasureInProcess.Visibility = System.Windows.Visibility.Hidden;
            measureTimer.Stop();

            // Propertys refreshen für GUI
            NotifyPropertyChanged("MeasuredLengthLeftForeArm");
            NotifyPropertyChanged("MeasuredLengthLeftUpperArm");
            NotifyPropertyChanged("MeasuredLengthLeftFullArm");
            NotifyPropertyChanged("MeasuredLengthLeftShank");
            NotifyPropertyChanged("MeasuredLengthLeftTigh");
            NotifyPropertyChanged("MeasuredLengthLeftLeg");

            if (KinectMeasurementCompleted != null)
                KinectMeasurementCompleted();
        }


        public event PropertyChangedEventHandler PropertyChanged;
        public event KinectMeasurementCompleted KinectMeasurementCompleted;

        // NotifyPropertyChanged will raise the PropertyChanged event, 
        // passing the source property that is being updated.
        public void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this,
                    new PropertyChangedEventArgs(propertyName));
            }
        }
    }

    public delegate void KinectMeasurementCompleted();

    public class DoubleToTrackedConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            double val = (double)value;
            if (val > 0)
                return "erkannt";
            else
                return "nicht erkannt";

        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

    public class Leg_DoubleToStringConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            double val = (double)value;
            if (val > 0)
                return string.Format("{0:F2}", val) + " cm";
            else
                return "0";

        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

    public class Arm_DoubleToStringConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            double val = (double)value;
            if (val > 0)
                return string.Format("{0:F2}", val) + " cm";
            else
                return "0";

        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

}
